{Titrator 3.01 is a chemical equilibrium calculator intended for use in aqueous }
{  systems with acid/base, complexation, dissociation, precipitation and }
{  electron transfer reactions. }

{  Titrator was designed primarily as a teaching tool for chemistry, }
{  biochemistry, geochemistry and environmental chemistry.  It is purely an }
{  equilibrium calculator, and lacks the database, transport, and kinetic  }
{  capabilities of more complex and sophisticated programs.  The user must }
{  provide the stoichiometry and log K value (or enthalpy and entropy) }
{  of each chemical species to be considered.}

{The algorithm is based upon the MicroQL logic of J. Westall, which in turn }
{   follows the algorithm in the MINEQL program of Westall and FMM Morel.}

{Currently documentation is incomplete, but may be useful and includes    }
{       TiManual        Users manual in .doc and .pdf                     }
{       TiTutorial      Tutorial based on a calcite system, .doc and .pdf }

{Current version 3.01, released under GPL by Steve Cabaniss, Dept. of Chemistry }
{  University of New Mexico, who can be contacted at cabaniss@unm.edu}

{*****************************************************************************}

{The unit TitrForm controls the initial screen of Titrator, also known as the }
{  system definition screen.  From this screen the user can create, edit, store}
{  and read a system definition, the basic data which define an equilibrium }
{  system.  The user may also solve the system (i.e., calculate the equilibrium}
{  concentrations), access the Sweep Concentration screen SweepForm to }
{  systematically vary one component or access the Simulate a Titration window}
{  to simullate a volumetric titration.}


unit TitrForm;

{$R+}                              {Range checks are enabled}

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, ComCtrls, TitrDefs, Solver, Menus, StdCtrls, Grids,
  CompEdit, SpecEdit, HeaderEdit, ParamEdit, Titrate, TitrHelp;

type
  TForm1 = class(TForm)
    MainMenu1: TMainMenu;
    File1: TMenuItem;
    SaveDefinition1: TMenuItem;
    ReadDefinition1: TMenuItem;
    Edit1: TMenuItem;
    Solve1: TMenuItem;
    Help1: TMenuItem;
    AboutTitrator1: TMenuItem;
    Topics: TMenuItem;
    CalculateEquilibriumConcentrations1: TMenuItem;
    AddComponent1: TMenuItem;
    AddSpecies1: TMenuItem;
    Exit1: TMenuItem;
    CompGrid: TStringGrid;
    SpeciesGrid: TStringGrid;
    Label1: TLabel;
    Label2: TLabel;
    GetSaveDefName: TSaveDialog;
    GetReadDefName: TOpenDialog;
    NewDefinition1: TMenuItem;
    EditDescription1: TMenuItem;
    SystemParameters1: TMenuItem;
    DataTitle: TLabel;
    SweepConc: TMenuItem;
    SweepBtn: TButton;
    SinglePtBtn: TButton;
    SimulateBtn: TButton;
    SimulateTitrate: TMenuItem;
    Label3: TLabel;
    SaveResults1: TMenuItem;
    GetSaveResultsName: TSaveDialog;
    ErrorAnalysis1: TMenuItem;

    procedure SaveDefinition1Click(Sender: TObject);
    procedure ReadDefinition1Click(Sender: TObject);
    procedure WriteCompGrid;
    Procedure WriteSpecGrid;
    Procedure WriteScreen;
    procedure Exit1Click(Sender: TObject);
    Procedure SolveOnce;
    procedure CalculateEquilibriumConcentrations1Click(Sender: TObject);
    procedure NewDefinition1Click(Sender: TObject);
    procedure CompGridClick(Sender: TObject);
    procedure SpeciesGridClick(Sender: TObject);
    procedure FormActivate(Sender: TObject);
    procedure SystemParameters1Click(Sender: TObject);
    procedure DataTitleClick(Sender: TObject);
    procedure EditDescription1Click(Sender: TObject);
    Procedure GoSweep;
    Procedure GoSimulate;
    procedure SweepConcClick(Sender: TObject);
    procedure SweepBtnClick(Sender: TObject);
    procedure SinglePtBtnClick(Sender: TObject);
    {procedure EditSpecies1Click(Sender: TObject); }
    procedure AddComponent1Click(Sender: TObject);
    procedure AddSpecies1Click(Sender: TObject);
    procedure AboutTitrator1Click(Sender: TObject);
    procedure TopicsClick(Sender: TObject);
    procedure SimulateBtnClick(Sender: TObject);
    procedure SimulateTitrateClick(Sender: TObject);
    procedure SaveResults1Click(Sender: TObject);
    procedure ErrorAnalysis1Click(Sender: TObject);

  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

uses Simulate, MCAccess;

{$R *.dfm}
{*******************************************************************}
{           The following routines handle file access               }
{*******************************************************************}

{SaveDefinition saves the data set S definition- }
{the lists of components and species, etc.}

procedure TForm1.SaveDefinition1Click(Sender: TObject);

Var
    F : TextFile;                     {ASCII text file}
    FileName : String;                {File name to save}
    SaveData : Boolean;               {Was a filename given by user?}
    Spe, com : integer;               {variable counters}
    i        : integer;               {text line counter}
    dummyint : integer;               {holds integer cast of types}


begin
  if ((S.NSpec <= 0) or (S.NComp <= 0)) then begin
       ShowMessage('You must have a complete definition to save.');
       Exit;
      end;

  SaveData := GetSaveDefName.Execute;      {get name of file to be saved}

  If SaveData then begin                   {if a name is chosen then...}

    If (ofExtensionDifferent in GetSaveDefName.Options) then begin
        ShowMessage('Please save as a tdf file.');
        Exit;
       end;

    FileName := GetSaveDefName.Filename;
    AssignFile(F, FileName);          { Open file for writing}
         {$I-}

    ReWrite(F);                       { start at the beginning }

    Writeln(F, S.head.title);
    for i := 1 to 3 do                { write title info }
       Writeln(F, S.head.descrip[i]);
    Writeln(F, S.NComp);              { write # of components, species}
    Writeln(F, S.NSpec);              { write # of species }

    Writeln(F, S.Temperature);        {Save value for temperature }
    If S.ThermoCalc then Writeln(F, 1){and whether thermodynamic calculations}
       else Writeln(F, 0);            {should be performed}

    Writeln(F, S.Mu);                 {Save value for ionic strength}
    If S.FindMu then Writeln(F, 1)    {and whether it is calculated}
       else Writeln(F, 0);            {or set by user}
    If S.UseMu then Writeln(F, 1)     {and whether it is to be used}
       else Writeln(F, 0);

    for com := 1 to S.NComp do       { write each component }
       with S.comps[com] do
         begin
           Writeln(F,Name,' ');      {name of component}

           dummyint := integer(Ctype);  {type of component}
           Writeln(F, dummyint);

           Write(F, Charge, ' ');
           Write(F, TotalX:14, ' ');
           Write(F, GuessX:14, ' ');
           WriteLn(F, StdDev:14);
         end;

    for spe := 1 to S.NSpec do                   { write species }
       with S.Specs[spe] do
         begin
           Writeln(F, Name, ' ');

           dummyint := integer(Stype);  {type of species}
           Writeln(F, dummyint);

           Write(F, DeltaH:14,' ');
           Write(F, DeltaS:14,' ');
           Write(F, DeltaG:14,' ');
           Write(F, LogK:14,' ');
           Write(F, StdDev:14,' ');
           for com := 1 to S.NComp do
             Write(F,' ',coeff[com]);
           Writeln(F);
         end;

    CloseFile(F);
   end;                           {end of 'if SaveData'; data set saved }

end;                              {end of procedure SaveDefinition1Click}

{SaveResults saves the results of a single calculation with data set S}
{formatted for human readability}

procedure TForm1.SaveResults1Click(Sender: TObject);

Var
    F : TextFile;                     {ASCII text file}
    FileName : String;                {File name to save}
    SaveData : Boolean;               {Was a filename given by user?}
    Spe, com : integer;               {variable counters}
    i        : integer;               {text line counter}
    PadName  : String;                {Component or species name padded }
                                      {with spaces out to 20 characters }

begin
  if ((S.NSpec <= 0) or (S.NComp <= 0)) then begin
       ShowMessage('You must have a complete definition to save.');
       Exit;
      end;

  If (Not Done) then begin
       ShowMessage('You must first solve the current system.');
       Exit;
     end;

  SaveData := GetSaveResultsName.Execute;      {get name of file to be saved}

  If SaveData then begin                   {if a name is chosen then...}

    If (ofExtensionDifferent in GetSaveResultsName.Options) then begin
        ShowMessage('Please save as a txt file.');
        Exit;
       end;

    FileName := GetSaveResultsName.Filename;
    AssignFile(F, FileName);          { Open file for writing}
         {$I-}

    ReWrite(F);                       { start at the beginning }


    {First write the data set title and descriptive information}
    Writeln(F, S.head.title);
    for i := 1 to 3 do                { write title info }
         Writeln(F, S.head.descrip[i]);

    {Then write the thermal controls}
    Writeln(F);
    If S.ThermoCalc then Writeln(F, 'Temperature corrected using Enthalpy')
       else Writeln(F, 'No temperature corrections performed');
    Writeln(F, 'Temperature (K) = ', S.Temperature:5:1); {Write Kelvin temp}

    {Then write the ionic strength controls}
    Writeln(F);
    If S.UseMu then begin
       Writeln(F, 'Ionic Strength corrections performed:');
       Write(F, 'Ionic strength = ', S.Mu:5:3);   {Write value for ionic strength}
       If S.FindMu then Writeln(F, ' Calculated') {and whether it is calculated}
         else Writeln(F, ' specified');           {or set by user}
      end
     else Writeln(F, 'No IS corrections used.');

    {Then, write the concentrations of components used}
    Writeln(F);
    WriteLn(F, 'Component            Type        Charge    Total(M)  Log_Free  Free_Molarity');
    for com := 1 to S.NComp do       { write each component }
       with S.comps[com] do
         begin
           PadName := Name + StringofChar(' ',20-Length(Name));
           Write(F, PadName,' ', CompTypeTitles[Ctype],' '); {name, type of component}
           Writeln (F, Charge:7, ' ', TotalX:12, ' ', FreeX:7:3, '   ', Conc:12);
         end;

    {and finally write the species concentrations}
    WriteLn(F);
    WriteLn(F, 'Species              Type        Molarity     Log_M   DeltaH DeltaS DeltaG    LogK   Stoichiometry');
    for spe := 1 to S.NSpec do                   { write species }
       with S.Specs[spe] do
         begin
           If (Conc > 0) then begin
           PadName := Name + StringofChar(' ',20-Length(Name));
           Write(F, PadName, ' ', SpecTypeTitles[Stype], ' ');
           Write(F, COnc:12, ' ', LogC:8:3, ' ', DeltaH:6:1,' ');
           Write(F, DeltaS:6:1,' ', DeltaG:6:1,' ', LogK:7:3,' ');
           for com := 1 to S.NComp do
             Write(F,' ',coeff[com]:2);
           Writeln(F);
          end;                        {end if}
         end;

    CloseFile(F);
   end;                           {end of 'if SaveData'; data set saved }

end;                              {end of procedure SaveResultsClick}




{Read Definition1Click reads a data set (list of components and species and }
{related information) from a file}

procedure TForm1.ReadDefinition1Click(Sender: TObject);

Var
    F : TextFile;                          {ASCII text file}
    FileName : String;                     {File name to read}
    ReadData : Boolean;                    {Was a filename given by user?}
    Spe, com : integer;                    {variable counters}
    i        : integer;                    {text line counter}
    dummyint : integer;                    {integer cast of declared types}

begin
   ReadData := GetReadDefName.Execute;      {get file name}

   If ReadData then begin                   {if a name is chosen then...}
     FileName := GetReadDefName.Filename;

     If (Not FileExists(FileName)) then begin    {check for non-existent file}
        ShowMessage('File not found.');
        Exit;
       end;

     If (ofExtensionDifferent in GetReadDefName.Options) then begin
        ShowMessage('You must select a tdf file.');
        Exit;
       end;

     AssignFile(F, FileName);               {Open file F for reading}

     {$I-}                                  {specifies IOResult (manual) }
                                            {handling of file errors }

    ReSet(F);                               {Go to the beginning of the file}

    With S do begin

    Readln(F, head.title);                { read title info }
    for i := 1 to 3 do
        readln(F, head.descrip[i]);
                                          { get number of components, species }
    Readln(F, NComp);
    Readln(F, NSpec);

    ReadLn(F, Temperature);            {read the temperature}
    ReadLn(F, dummyint);
    If (dummyint = 1) then ThermoCalc := true
       else thermocalc := False;

    Readln(F, Mu);                     {read the ionic strength}
    ReadLn(F, dummyint);
    If (dummyint = 1) then FindMu := true
       else FindMu := False;
    ReadLn(F, dummyint);
    If (dummyint = 1) then UseMu := true
       else UseMu := False;

       for com := 1 to NComp do begin
          Comps[com] := BlanComp;
          with comps[com] do                    { read components }
            begin
              readln(F, Name);
              Readln(F, dummyint);
              Ctype := CompType(dummyint);
              read(F, Charge);
              read(F, TotalX);
              read(F, GuessX);
              Readln(F, StdDev);
              {ShowMessage('StdDev = ' + FloatToStrF(StdDev, ffExponent, 4,8));}
            end;
           end;

       for spe := 1 to NSpec do begin           { read species }
          Specs[spe] := BlanSpec;
          with Specs[spe] do
            begin
              readln(F, Name);
              Readln(F, dummyint);
              Stype := SpecType(dummyint);

              Read(F, DeltaH);
              Read(F, DeltaS);
              Read(F, DeltaG);
              read(F, LogK);
              Read(F, StdDev);
              for com := 1 to NComp do
                read(F, coeff[com]);
              Readln(F);
            end;
           end;
       end;                                     {end of with statement}
       CloseFile(F);                            { data set read OK }
       SortComp;
       SortSpec;
                
       WriteScreen;
       Done := False;                           {problem has not been solved}
    end;


  end;                                          { end of ReadDefinition1Click}


{***************************************************************************}
{               The following procedures are used to display                }
{                the components and species in the data set                 }
{***************************************************************************}


 { The WriteComp procedure writes the data for component Com into }
 {the string grid CompGrid for display purposes}

Procedure WriteComp(DisComp: Component;
                    Com : integer);

begin
   With Discomp do begin
     Form1.CompGrid.Cells[0, com] := Name;
     Form1.CompGrid.Cells[1, Com] := CompTypeTitles[CType];
     Form1.CompGrid.Cells[2, Com] := FloatToStrF(TotalX,ffExponent,4,3);
     Form1.CompGrid.Cells[3, Com] := FloatToStrF(Conc,ffExponent,4,3);
     Form1.CompGrid.Cells[4, Com] := FloatToStrF(FreeX,ffFixed,8,3);
     Form1.CompGrid.Cells[5, Com] := FloatToStrF(GuessX, ffFixed, 8, 3);
    If (CType = CTotDiss) then
     Form1.CompGrid.Cells[6, com] := FloatToStrF(StdDev, ffExponent, 4,8)
    else
     Form1.CompGrid.Cells[6, com]:= FloatToStrF(StdDev, ffFixed, 8, 3);
     Form1.CompGrid.Cells[7, Com] := FloatToStrF(Error,ffExponent,4,3);
     Form1.CompGrid.Cells[8, Com] := FloatToStrF(Charge,ffFixed,3,0);
  end;                                       {End of With}
end;                                         {End of WriteComp}

{WriteCompGrid writes the entire set of components to the data grid COmpGrid}

Procedure TForm1.WriteCompGrid;

Var
   Com : integer;
   Xbox : integer;
   CD  : CompDisp;

begin
   Form1.CompGrid.RowCount := S.NComp + 2;

   Xbox := 0;
   For CD := CompName to CCharge do begin
       Form1.CompGrid.Cells[Xbox, 0] := CompTitles[CD];
       Xbox := XBox + 1;
    end;

   For Com := 1 to S.NComp do
        WriteComp(S.Comps[Com], Com);

   For XBox := 0 to 8 do
       Form1.CompGrid.Cells[Xbox, S.NComp +1] := '';


end;

{EraseCompGrid writes blank values to the Component Grid}
Procedure EraseCompGrid;
var com, i : integer;
 begin
  For com := 0 to S.NComp do
    For i := 0 to 8 do
        Form1.CompGrid.Cells[i, com] := '';

 end;                               {End EraseCompGrid}

 { The WriteSpec procedure writes the data for Species Spe into }
 { the string grid SpeciesGrid for display purposes}

Procedure WriteSpec(DisSpec: Specie;     {Species to display}
                    Spe : integer);      {Grid Row to display it in }

Var   com : integer;                         {Component counter}
      dummy : string;

begin
   With DisSpec do begin
   Form1.SpeciesGrid.Cells[0, spe] := Name;
   Form1.SpeciesGrid.Cells[1, spe] := SpecTypeTitles[SType];
   Form1.SpeciesGrid.Cells[2, spe] := FloatToStrF(Conc,ffExponent,4,3);
   Form1.SpeciesGrid.Cells[3, spe] := FloatToStrF(LogC, ffFixed, 8,3);
   Form1.SpeciesGrid.Cells[4, spe] := FloatToStrF(LogK, ffFixed,8,3);
   Form1.SpeciesGrid.Cells[5, spe] := FloatToStrF(StdDev, ffFixed,8,3);
   Form1.SpeciesGrid.Cells[6, spe] := FloatToStrF(DeltaH, ffFixed,8,3);
   Form1.SpeciesGrid.Cells[7, spe] := FloatToStrF(DeltaS, ffFixed,8,3);
   Form1.SpeciesGrid.Cells[8, spe] := FloatToStrF(DeltaG, ffFixed,8,3);

   dummy := '';
   Charge := 0;
   For com := 1 to S.NComp do begin
     if (coeff[com] > 1) then
         dummy := dummy + ' + ' + IntToStr(coeff[com]) +
         '(' + S.Comps[com].Name +')';
     if (coeff[com] = 1) then
         dummy := dummy + ' + ' + S.Comps[com].Name;
     if (coeff[com] = -1) then
         dummy := dummy + ' - ' + S.Comps[com].Name;
     if (coeff[com] < -1) then
         dummy := dummy +' '+IntToStr(coeff[com])+'(' + S.Comps[com].Name + ')';
     Charge := Charge + coeff[com] * S.Comps[com].Charge;
   end;                         {end of for loop}

   Form1.SpeciesGrid.Cells[9, spe] := IntToStr(Charge);
   Form1.SpeciesGrid.Cells[10, spe] := dummy;

  end;                                       {End of With}
end;                                         {End of WriteSpec}

{WriteSpecGrid writes the entire set of species to the data grid SpeciesGrid}

Procedure TForm1.WriteSpecGrid;

Var
   Spe : integer;       {counts species}
   Xbox : integer;      {counts grid columns}
   SD  : SpecDisp;

begin
   Form1.SpeciesGrid.RowCount := S.NSpec + 2;

   Xbox := 0;
   For SD := SpecName to SStoichiometry do begin
       Form1.SpeciesGrid.Cells[Xbox, 0] := SpecTitles[SD];
       Xbox := XBox + 1;
    end;

  For Spe := 1 to S.NSpec do
        WriteSpec(S.Specs[spe], spe);

  For Xbox := 0 to (4 + S.NComp) do
        Form1.SpeciesGrid.Cells[Xbox, S.NSpec + 1] := '';

end;                                             {End of WriteSpecGrid}

{EraseSpecGrid writes blank values to the Species Grid}

Procedure EraseSpecGrid;
var spe, i : integer;
 begin
  For spe := 0 to S.NSpec do
     For i := 0 to (4 + S.NComp) do
        Form1.SpeciesGrid.Cells[i, spe] := '';

 end;                               {End EraseSpecGrid}

{WriteScreen Writes the title, component list and species list to the screen}

 Procedure TForm1.WriteScreen;

 begin
    Form1.DataTitle.Caption := S.Head.Title;
    Form1.WriteCompGrid;
    Form1.WriteSpecGrid;
 end;


{****************************************************************************}
{                                                                            }
{The following routines respond to specific menu selections or action buttons}
{                                                                            }
{****************************************************************************}

{ The Exit button terminates the application with no 'clean up'}

procedure TForm1.Exit1Click(Sender: TObject);
begin
     Application.Terminate;
end;                                            {End of Exit1Click}

{The CalculateEquilibriumConcentrations solves the current equilibrium problem}
{using routines found in Solver}

Procedure TForm1.SolveOnce;

Var dummy : String;

begin
  if ((S.NSpec <= 0) or (S.NComp <= 0)) then
     ShowMessage('You must have a complete definition before solving.')
     else begin
       SortComp;
       SortSpec;
       If S.ThermoCalc then
         If (Not ThermoCorr) then
            Showmessage('Not all species Ks could be calculated.');
       InitArrays(True);
       Done := SolidSolve;                  { Current Data set }
       Replace;                             { and Display Results }

       WriteCompGrid;
       WriteSpecGrid;

       If (PhaseViol) then
         Showmessage('Phase rule violation- results invalid.');
       If (TooMany) then
         Showmessage('Iteration did not converge- results may be incorrect.');
       If (MatError) then
         ShowMessage('Matrix not solvable- system is not correctly specified.');
       If (Done) then begin
         dummy := 'Success! Calculation converged in '+ IntToStr(Iter) +' iterations.';
         Showmessage(dummy);
        end;
     end;

end;           {End of SolveOnce}

procedure TForm1.CalculateEquilibriumConcentrations1Click(Sender: TObject);
begin
  SolveOnce;
end;

{****************************************************************************}
{      The following procedures are used to edit data sets                   }
{****************************************************************************}

{Procedure RemoveComp deletes the component # com from the data set }

Procedure RemoveComp(Com : integer);
var  i,j : integer;                    {loop counters}

begin
    {reduce number of components by 1}
    for i := com to S.NComp do
        begin
          S.comps[i] := S.comps[i+1];       {move each component down 1 in list}
          for j := 1 to S.NSpec do          {eliminate coeffs for deleted comp}
             S.Specs[j].coeff[i] := S.Specs[j].coeff[i+1];
        end;

    S.comps[S.NComp] := BlanComp;
    S.NComp := S.NComp - 1;
    Done := False;
end;                                 {End RemoveComp}

{Procedure RemoveSpec deletes the species # spe from the data set }

Procedure RemoveSpec(Spe : integer);
var  i : integer;                    {loop counter}

begin
                    {reduce number of species by 1}
    for i := spe to S.NSpec do
        begin
          S.specs[i] := S.specs[i+1];       {move each species down 1 in list}
         end;

    S.Specs[S.NSpec] := BlanSpec;
    S.NSpec := S.NSpec - 1;
    Done := False;
end;                                 {End RemoveSpec}


{The procedure CompGridClick activates component editing}
{Clicking on an existing component allows user to change or delete }
{     that component.}
{CLicking on the empty row allows user to add a new component}

procedure TForm1.CompGridClick(Sender: TObject);

begin
  If (CompGrid.Row <= S.NComp) then begin   {User clicked on existing component}
     EditC := CompGrid.Row;                 {get component number}
     EdComp := S.Comps[EditC];              {and copy the component into EdComp}
     EdMode := Edit;                        {Set editing mode }
     CompForm.Showmodal;                    {and activate the component editor}
    end
     Else If (CompGrid.Row = (S.NComp + 1)) then  {User indicates new component}
      begin
       EditC := CompGrid.Row;
       EdComp := BlanComp;                       {Set EdComp to default (blank)}
       EdMode := Add;                            {Set editing mode to add new}
       CompForm.ShowModal;                       {and activate component editor}
      end;

  {Control returns only when the component editor is finished}
  {Now the EdMode variable can have 4 values }
  {   Add- Add the contents of EdComp as a new component}
  {   Edit - replace the current component #EditC with EdComp}
  {   Delete - delete the indicated component }
  {   None - take no action, since user cancelled changes }

  Case EdMode of

  Add: begin              {User has chosen to add a new component to data set S}
      EraseCompGrid;      {Erase previous displays}
      EraseSpecGrid;
      S.comps[EditC] := EdComp;   {Append new component to the end of the list}
      S.NComp := EditC;           {increase number of components}
      WriteCompGrid;              {and re-write the display}
      WriteSpecGrid;
     end;

  Edit: begin                    {User has chosen to change a component's }
       S.comps[EditC] := EdComp; {properties-replace old comp with new version}
       WriteComp(EdComp, EditC);
      end;

  Delete: begin                      {User has chosen to delete a component}
           EraseCompGrid;            {Erase previous data set display}
           EraseSpecGrid;
           RemoveComp(CompGrid.Row); {Delete the component}
           WriteCompGrid;            {and then re-write the display grids}
           WriteSpecGrid;
         end;
     end;

  Done := False;
end;                       {End of procedure CompGridClick}


{The procedure SpeciesGridClick activates species editing}
{Clicking on an existing species allows user to change or delete }
{     that species.}
{CLicking on the empty row allows user to add a new species}

procedure TForm1.SpeciesGridClick(Sender: TObject);
begin
 If (SpeciesGrid.Row <= S.NSpec) then begin {User clicked on existing species}
     EditS := SpeciesGrid.Row;              {get species number}
     EdSpec := S.Specs[EditS];              {and copy the species into EdSpec}
     EdMode := Edit;                        {Set editing mode }
     SpecForm.Showmodal;                    {and activate the species editor}
    end
     Else If (SpeciesGrid.Row = (S.NSpec + 1)) then  {User indicates new species}
      begin
       EditS := SpeciesGrid.Row;
       EdSpec := BlanSpec;                       {Set EdSpec to default (blank)}
       EdMode := Add;                            {Set editing mode to add new}
       SpecForm.ShowModal;                       {and activate species editor}
      end;

  {Control returns only when the cspecies editor is finished}
  {Now the EdMode variable can have 4 values }
  {   Add- Add the contents of EdSpec as a new species}
  {   Edit - replace the current component #EditS with EdSpec}
  {   Delete - delete the indicated species }
  {   None - take no action, since user cancelled changes }

  Case EdMode of

  Add: begin              {User has chosen to add a new species to data set S}
      S.Specs[EditS] := EdSpec;   {Append new species to the end of the list}
      S.NSpec := EditS;           {increase number of species}
      WriteSpecGrid;              {and re-write the display}
     end;

  Edit: begin                    {User has chosen to change a species' }
       S.Specs[EditS] := EdSpec; {properties-replace old spec with new version}
       WriteSpec(EdSpec, EditS);
      end;

  Delete: begin                      {User has chosen to delete a species}
           EraseSpecGrid;            {Erase previous data set display}
           RemoveSpec(EditS);        {Delete the species}
           WriteSpecGrid;            {and then re-write the display grid}
         end;
     end;

   Done := False;
end;


{The procedure NewDefinition1Click erases the contents of the Species and
{Component grids, then re-initializes variables to default values}

procedure TForm1.NewDefinition1Click(Sender: TObject);

begin
    EraseSpecGrid;              {First erase current cells }
    EraseCompGrid;

    DefaultDefn;                {Then create a default (empty) data set }
    {DefaultTitr;                {and reset the titration parameters }

    WriteScreen;                {and write it to the screen}
    Done := False;
end;

{This procedure simply writes the current system definition to }
{the screen when the form is activated}

procedure TForm1.FormActivate(Sender: TObject);
begin
    WriteScreen;
end;

{This menu item allows the user to edit the system parameters, including
iteration control, temperature control and ionic strength control}

procedure TForm1.SystemParameters1Click(Sender: TObject);
begin
    ParamForm.ShowModal;
    Done := False;
end;

{Clicking on the title of the system definition allows the user
{to change the title and header information for the current system definition }

procedure TForm1.DataTitleClick(Sender: TObject);
begin
    HeaderForm.ShowModal;
    Form1.DataTitle.Caption := S.Head.Title;
end;

{This menu item allows the user to change the title and header information}
{for the current system definition}

procedure TForm1.EditDescription1Click(Sender: TObject);
begin
    HeaderForm.ShowModal;
    Form1.DataTitle.Caption := S.Head.Title;
end;

{GoSweep activates the Sweep form to allow the user to systematically vary a}
{ component constraint, providing a valid, solved equilibrium is present}

Procedure TForm1.GoSweep;
begin
   If ((S.NComp <= 0) or (S.NSpec <= 0 )) then begin
      Showmessage('You must first define an equilibrium system.');
      Exit;
     end;
   If (Not Done) then begin
       ShowMessage('You must solve the current system before Sweeping.');
       Exit;
     end;
   SweepForm.ShowModal

 end;                           {End of GoSweep}

{GoSimulate activates the Simulate Titration form to allow the user to
{simulate a titration providing a valid, solved equilibrium is present}

Procedure TForm1.GoSimulate;
begin
   If ((S.NComp <= 0) or (S.NSpec <= 0 )) then begin
      Showmessage('You must first define an equilibrium system.');
      Exit;
     end;
   If (Not Done) then begin
       ShowMessage('You must solve the current system before titrating.');
       Exit;
     end;
   SimulateForm.ShowModal

 end;                           {End of GoSweep}


{This menu item takes the user to the Sweep Concentration screen, }
{provided an equilibrium system has been defined}

procedure TForm1.SweepConcClick(Sender: TObject);
begin
  GoSweep;
end;

{Clicking on the Titrate Button takes the user to the Titrate screen, }
{provided an equilibrium system has been defined}

procedure TForm1.SweepBtnClick(Sender: TObject);
begin
  GoSweep;
end;

{Clicking the 'Solve this system' button calculates equilibrium concentration}
{for the currently defined system}

procedure TForm1.SinglePtBtnClick(Sender: TObject);
begin
   SolveOnce;
end;

{procedure TForm1.EditSpecies1Click(Sender: TObject);
begin

end; }

{Menu-based addition of a component}

procedure TForm1.AddComponent1Click(Sender: TObject);
begin
   EditC := S.NComp + 1;         {new component would go in this slot}
   EdComp := BlanComp;           {Set EdComp to default (blank)}
   EdMode := Add;                {Set editing mode to add new}
   CompForm.ShowModal;           {activate component editor}

   If (EdMode = Add) then begin  {unless user has decided to cancel}
     S.comps[EditC] := EdComp;   {Append new component to the end of the list}
     S.NComp := EditC;           {increase number of components}
     WriteCompGrid;              {and re-write the display}
     WriteSpecGrid;
    end;
end;

{Menu-based addition of a species}

procedure TForm1.AddSpecies1Click(Sender: TObject);
begin
    EditS := S.NSpec + 1;
    EdSpec := BlanSpec;                       {Set EdSpec to default (blank)}
    EdMode := Add;                            {Set editing mode to add new}
    SpecForm.ShowModal;                       {and activate species editor}
    If (EdMode = Add)  then begin
      S.Specs[EditS] := EdSpec;   {Append new species to the end of the list}
      S.NSpec := EditS;           {increase number of species}
      WriteSpecGrid;              {and re-write the display}
     end;

end;

procedure TForm1.AboutTitrator1Click(Sender: TObject);
begin
   ShowMessage('Titrator Chemical Equilibrium Calculator  v. 3.00 November 2008  SE Cabaniss');
end;

{Clicking 'Topics' displays a non-standard help form, TitrHelp }

procedure TForm1.TopicsClick(Sender: TObject);
begin
   TitrHelpForm.ShowModal;
 end;

procedure TForm1.SimulateBtnClick(Sender: TObject);
begin
   GoSimulate;
end;

procedure TForm1.SimulateTitrateClick(Sender: TObject);
begin
  GoSimulate;
end;


procedure TForm1.ErrorAnalysis1Click(Sender: TObject);

begin
   If ((S.NComp <= 0) or (S.NSpec <= 0 )) then begin
      Showmessage('You must first define an equilibrium system.');
      Exit;
     end;
   If (Not Done) then begin
       ShowMessage('You must first solve the current system.');
       Exit;
     end;
   If (Not EnableMonte) then begin
       ShowMessage('Error analysis is not yet available.');
       Exit;
     end;
   MCForm.ShowModal;
 end;                           {End of ErrorAnalysis1Click}

initialization              { This section initializes the variables }

 begin
    DefaultDefn;
    DefaultParams;
    DefaultMC;
    GetDir(0, HelpDir);               {Gets the location of the program and }
    HelpDir := HelpDir + '/';                                  {Help files }
    
 end;
                                                {End of TitrForm}
end.


